package com.yzg.simple.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @author misterWei
 * @mailbox mynameisweiyan@gmail.com
 *  NIO 网络传输介质
 * 结合 selector channel Buffer 组合使用 成立服务端
 */
public class NioServer {

    public static void main(String[] args)throws Exception {
        //创建服务端管理渠道传输
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //创建Selector对象 对channel 进行管理
        Selector selector = Selector.open();

        //服务器端绑定端口号
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));

        //设置为非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //将服务器渠道注册到选择器中 OP_ACCEPT 是创建来链接的时候进行事件驱动处理
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true){
            // 阻塞一秒时间
            if (selector.select(1000) ==0) {
                System.out.println("服务器阻塞一秒,并未得到链接");
                continue;
            }

            // 如果往下走 就是已经创建连接了,就需要判断当前是什么事件驱动去做对应的逻辑处.
            Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator();
            //得到所有的SelectionKey  根据SelectionKey 去得到那个事件.
            while (selectionKeyIterator.hasNext()){
                SelectionKey selectionKey = selectionKeyIterator.next();
                //第一次链接,创建一个socketChannel到注册到客户端
                if (selectionKey.isAcceptable()){
                    // accept 在BIO是用来阻塞的,但是在以上逻辑判断中是显然有数据的.不会阻塞
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    // 设置为非阻塞
                    socketChannel.configureBlocking(false);
                    //注册到Selector 中,设置新的事件驱动,并为当前Channel 注册一个Buffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    System.out.println("客户端第一次链接 注册到Selector" +socketChannel.getLocalAddress());
                }

                // 如果是读的事件驱动
                if (selectionKey.isReadable()){
                    // 反向推 Channel
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    //这个Buffer 就是第一次事件驱动所注册的ByteBuffer 对象.
                   ByteBuffer byteBuffer =  (ByteBuffer) selectionKey.attachment();
                   socketChannel.read(byteBuffer);
                   byteBuffer.flip();
                    System.out.println("客户端发出的消息 : "+ new String(byteBuffer.array()));
                }
                // 把当前SelectorKey 删除,避免重复操作
                selectionKeyIterator.remove();

            }
        }

    }
}
